<template>
  <div
    class="flex items-center gap-1 rounded px-2 py-0.5 hover:bg-slate-200/40 dark:hover:bg-white/10 cursor-pointer min-w-0 select-none border-l-2 border-transparent transition-colors duration-150"
    :class="[
      (selected || focused) 
        ? 'bg-accent/15 dark:bg-accent/25 border-accent dark:border-accent/80' 
        : '', 
      animateChanges ? 'tv-anim' : ''
    ]"
    :style="{ paddingLeft: (item.depth * indent) + 'px', height: rowHeight + 'px' }"
    role="treeitem"
    :aria-level="item.depth + 1"
    :aria-expanded="isDirectory ? String(isExpanded) : undefined"
    @click.stop="$emit('click', item, $event)"
    @dblclick.stop="$emit('dblclick', item, $event)"
    @contextmenu.prevent.stop="$emit('contextmenu', item, $event)"
    :draggable="draggable"
    @dragstart="$emit('dragstart', item, $event)"
    @dragover="$emit('dragover', item, $event)"
    @dragleave="$emit('dragleave', item, $event)"
    @drop="$emit('drop', item, $event)"
    @dragend="$emit('dragend', item, $event)"
  >
    <!-- 展开/折叠按钮 -->
    <template v-if="isDirectory">
      <button
        class="inline-flex h-4 w-4 items-center justify-center text-slate-500 dark:text-neutral-500"
        :aria-label="isExpanded ? 'Collapse' : 'Expand'"
        :aria-expanded="isExpanded"
        @click.stop="$emit('toggle', item)"
      >
        <svg 
          viewBox="0 0 24 24" 
          fill="none" 
          stroke="currentColor" 
          stroke-width="1.8" 
          stroke-linecap="round" 
          stroke-linejoin="round"
          :class="['h-3 w-3 transition-transform transform', isExpanded ? 'rotate-90' : 'rotate-0']"
        >
          <path d="M9 6l6 6-6 6" />
        </svg>
      </button>
    </template>
    <span v-else class="inline-flex h-4 w-4"></span>

    <!-- 前缀插槽 -->
    <slot name="prefix" :node="item.node" :depth="item.depth"></slot>

    <!-- 文件图标 -->
    <span v-if="!isDirectory" class="inline-flex items-center justify-center ml-1 mr-1">
      <FileIcon 
        :name="String(item.node.label || item.node.id || '')" 
        :size="16" 
        :isDir="false" 
      />
    </span>

    <!-- 节点标签 -->
    <span v-if="!isEditing" class="truncate" :title="item.displayLabel">
      <!-- 压缩路径显示 -->
      <template v-if="isCompressedPath">
        <template v-for="(seg, i) in compressedSegments" :key="i">
          <span :class="i < (compressedSegments.length - 1) ? 'text-slate-500 dark:text-neutral-400' : ''">
            <template v-for="part in getHighlightParts(seg)" :key="part.text + String(part.hit)">
              <span :class="part.hit ? 'bg-yellow-300/30 dark:bg-yellow-400/40 text-gray-900 dark:text-yellow-100 font-medium' : ''">{{ part.text }}</span>
            </template>
          </span>
          <span v-if="i < (compressedSegments.length - 1)" class="mx-0.5 text-slate-400">{{ compressSeparator }}</span>
        </template>
      </template>
      <!-- 普通标签显示 -->
      <template v-else>
        <template v-for="part in getHighlightParts(item.displayLabel)" :key="part.text + String(part.hit)">
          <span :class="part.hit ? 'bg-yellow-300/30 dark:bg-yellow-400/40 text-gray-900 dark:text-yellow-100 font-medium' : ''">{{ part.text }}</span>
        </template>
      </template>
    </span>
    
    <!-- 内联编辑组件 -->
    <InlineRename 
      v-if="isEditing"
      :editing-mode="editingMode"
      :editing-label="editingLabel"
      @commit="$emit('commit-inline')"
      @cancel="$emit('cancel-inline')"
      @update:label="$emit('update:editing-label', $event)"
      @set-input="$emit('set-editing-input', $event)"
    />

    <!-- 后缀插槽 -->
    <slot name="suffix" :node="item.node" :depth="item.depth"></slot>
  </div>
</template>

<script setup>
import { computed } from 'vue';
import FileIcon from './FileIcon.vue';
import InlineRename from './InlineRename.vue';

// Props - 简化为纯渲染所需的属性
const props = defineProps({
  item: { type: Object, required: true }, // { node, depth, displayLabel, id }
  index: { type: Number, required: true },
  indent: { type: Number, default: 12 },
  rowHeight: { type: Number, default: 22 },
  selected: { type: Boolean, default: false },
  focused: { type: Boolean, default: false },
  editingNodeId: { type: String, default: null },
  editingMode: { type: String, default: null },
  editingLabel: { type: String, default: '' },
  filterText: { type: String, default: '' },
  compressSeparator: { type: String, default: '/' },
  animateChanges: { type: Boolean, default: false },
  draggable: { type: Boolean, default: true },
  getIcon: { type: Function, default: null },
});

// Events - 简化为纯事件传递
const emit = defineEmits([
  'click',
  'dblclick', 
  'contextmenu',
  'toggle',
  'dragstart',
  'dragover',
  'dragleave',
  'drop',
  'dragend',
  'commit-inline',
  'cancel-inline',
  'update:editing-label',
  'set-editing-input'
]);

// 计算属性 - 只保留渲染相关的计算
const isDirectory = computed(() => {
  const node = props.item.node;
  return node && (node.isDir === true || node.is_dir === true || node.isFolder === true);
});

const isExpanded = computed(() => {
  // 这里需要从父组件传入展开状态，或者通过其他方式获取
  // 暂时简化处理
  return props.item.expanded || false;
});

const isEditing = computed(() => {
  return String(props.item.node.id) === String(props.editingNodeId);
});

const isCompressedPath = computed(() => {
  return (props.item.displayLabel || '').includes(props.compressSeparator);
});

const compressedSegments = computed(() => {
  if (!isCompressedPath.value) return [];
  return (props.item.displayLabel || '').split(props.compressSeparator);
});

// 工具函数 - 只保留渲染相关的函数
function getHighlightParts(text) {
  const q = String(props.filterText || '').toLowerCase();
  const s = String(text || '');
  if (!q) return [{ text: s, hit: false }];
  
  const lower = s.toLowerCase();
  const parts = [];
  let i = 0;
  
  while (i < s.length) {
    const idx = lower.indexOf(q, i);
    if (idx === -1) { 
      parts.push({ text: s.slice(i), hit: false }); 
      break; 
    }
    if (idx > i) parts.push({ text: s.slice(i, idx), hit: false });
    parts.push({ text: s.slice(idx, idx + q.length), hit: true });
    i = idx + q.length;
  }
  
  return parts;
}
</script>

<style scoped>
/* 行级淡入动画，用于刷新/rehydrate 时减少突兀变化 */
.tv-anim { 
  animation: tv-fade-in 140ms ease; 
  will-change: opacity, transform; 
}

@keyframes tv-fade-in {
  from { 
    opacity: 0.01; 
    transform: translateY(2px); 
  }
  to { 
    opacity: 1; 
    transform: translateY(0); 
  }
}

@media (prefers-reduced-motion: reduce) {
  .tv-anim { 
    animation: none; 
  }
}
</style>